<?php
namespace app\manage\service;

use app\manage\service\ApiSuper;
/**
 * api中除服务类以外的所有类的父类,
 * 在其子类中,应避免使用$_GET,$_POST,及cookie中的数据,所有的数据应从类属性中获得
 * @author   李全春   <adv30@126.com>  <time:2016年4月17日14时31分50秒>
 *
 */
class ApiBase extends ApiSuper
{

    protected $jsonData  = null;     //app传过来的非文件数据,默认为json字符串转成的josn对象
    protected $arrData   = null;     //app传过来的非文件数据,默认为json字符串转成的array

    // 逻辑层池
    protected $logicPond = [];

    public function __construct($token = null, $textData = null, $fileData = null, $encryption = null )
    {
        if( !is_null($textData) ){
            // $this->textData = $textData;
            // $this->jsonData = json_decode( $textData );
            $this->arrData  = json_decode( $textData, TRUE );
            $this->arrData['token'] = $token;
        }
        $this->fileData   = $fileData;
        //prw( $textData, $this->jsonData, $this->arrData );
        $this->encryption = $encryption;
        $this->token = $token;
    }
    // stepRun oneStepRun 的功能组合部分
    private function stepRunFun(array $data, string $auths, string $scene = '', string $success = '1', string $validate = '', string $logic = ''): array
    {
        // 如果验证层类名或逻辑层类名为空
        if ('' === $validate || '' === $logic){
            $classArr = explode('\\', get_class($this));
            $class = lcfirst($classArr[count($classArr) - 1]);
            if ('' === $validate){
                $validate = $this->{$class . 'Validate'};
            }
            if ('' === $logic) {
                $logic = $this->{$class . 'Logic'};
            }
        }
        // 进行数据验证
        $validateObj = validate($validate);
        if (! ($validateObj->scene($scene)->check($data))) {
            return verify_err($data, $validateObj->getError());
        }
        // 进行权限验证
        if (CHECK_AUTH) {
            $authRes = $this->checkAuths($auths);
            if (SUCCESS_STATUS !== $authRes['status']) {
                return $authRes;
            }
        }

        return auto_success(['logic' => $logic], '成功获取要发送的数据');
    }
    /**
     * 根据token验证权限
     * {"token":"b49baca86cf59b174feae087a63d8656", "auths":["serveruser/auth/Token/check","serveruser/user/User/login"]}
     */
    protected function checkAuths($auths): array
    {
        // 如果是字符串类型 将字符串进行分隔成数组
        // 如果数组,直接时行验证
        if(is_string($auths)) {
            $auths = explode(',', $auths);
        } else if (!is_array($auths)){
            return verify_err($auths, '$path只能是数组或字符串类型');
        }
        $checkData['query'] = json_encode([
            ['path' => 'serveruser/auth/Token/checkAuths', 'param' => ['token' => $this->token, 'auths' => $auths]],
        ]);
        $checkData['success'] = '1';
        return self::sendDataOne(config('proxy_queue_url'), $checkData);
    }
    // 自动执行验证和逻辑层
    protected function stepRun(array $data, string $path, string $scene = '', string $methods = '', string $success = '1', string $validate = '', string $logic = ''): array
    {
        $runDataRes = $this->stepRunFun($data, $path, $scene, $success, $validate, $logic);
        if (SUCCESS_STATUS != $runDataRes['status']) {
            return $runDataRes;
        }
        return $this->logic($runDataRes['res']['logic'])->$methods($data);
    }

    //封装单个请求的方法
    public function oneStepRun(array $data, string $path, string $scene = '', string $success = '1', string $validate = '', string $logic = ''): array
    {

        $runDataRes = $this->stepRunFun($data, $path, $scene, $success, $validate, $logic);
        if (SUCCESS_STATUS != $runDataRes['status']) {
            return $runDataRes;
        }
        // 封装数据
        $sendData['query'] = json_encode([['path'=>$path, 'param'=>$data]]);
        $sendData['success'] = $success;
        return self::sendDataOne(config('proxy_queue_url'), $sendData);
    }

    //封装并发多个请求的方法  $data用于验证器验证数据 queryData为json_encode()方法里的参数 $path为多个逗号隔开的需要权限验证的字符串
    public function lotsStepRun(array $data, array $queryData, string $path, string $scene = '', string $success = '1', string $validate = '', string $logic = ''): array
    {

        $runDataRes = $this->stepRunFun($data, $path, $scene, $success, $validate, $logic);
        if (SUCCESS_STATUS != $runDataRes['status']) {
            return $runDataRes;
        }
        // 封装数据
        $sendData['query'] = json_encode($queryData);
        $sendData['success'] = $success;
        return self::sendDataLots(config('proxy_all_url'), $sendData);
    }

    // 返回逻辑层
    protected function logic (string $path): \app\manage\logic\BaseLogic
    {
        $key = md5($path);
        return $this->logicPond[$key] ?? $this->logicPond[$key] = logic($path);
    }

    /*
    参数形式： ['name' => '默认名称']
    等同： isset($this->arrData['name']) && $data['name'] = $this->arrData['name'] ? $this->arrData['name'] : '默认名称';
    */
    public function issetReceiveData(array $default = [], array $val=['0', 0], bool $type=true): array
    {
        $res = [];
        foreach ($default as $k => $v) {
            if(isset($this->arrData[$k])){
                if(in_array($this->arrData[$k], $val, $type)){
                    $res[$k] = $this->arrData[$k];
                }else{
                    $res[$k] = $this->arrData[$k] ? $this->arrData[$k] : $v;
                }
            }
        }
        return $res;
    }

    /*
    参数形式： ['name' => '默认名称']
    等同： $data['name'] = $this->arrData['name'] ?? '默认名称';   //用于：该name字段可以不传，但需要添加name值
    */
    public function haveIssetReceiveData(array $default = [], array $val = ['0', 0 , ''], $type = true): array
    {
        $res = [];
        foreach ($default as $k => $v) {
            $res[$k] = isset($this->arrData[$k]) && !in_array($this->arrData[$k], $val, $type) ? $this->arrData[$k] : $v;
        }
        return $res;
    }
    /*
    参数形式： ['name' => '默认名称']
    等同：$data['name'] = $this->arrData['name'] ? $this->arrData['name'] : '默认名称';
    */
    public function receiveData(array $default = [], array $val=['0', 0], bool $type=true): array
    {
        $res = [];
        foreach ($default as $k => $v) {
            if(in_array($this->arrData[$k], $val, $type)){
                $res[$k] = $this->arrData[$k];
            }else{
                $res[$k] = $this->arrData[$k] ? $this->arrData[$k] : $v;
            }
        }
        return $res;
    }

    /*
     * 将传入的字段数组 根据转为另一个如 ['aaa' => 'ccc'] => ['bbb' => 'ccc']
     * @param $default ['aaa' => 'bbb'] 存转换前的键和转换后的键
     * @param $beforeAction 在接收此方法之前,预处理方法
     * @param #beforeParam 预处理参数据
     */
    public function changeKeyData(array $default = [], string $beforeAction = '', array $beforeParam = []): array
    {
        $data = $this->arrData;
        if ($beforeAction !== '' && is_callable([$this, $beforeAction])) {
            $data = $this->$beforeAction($beforeParam);
        }

        $res = [];
        foreach ($default as $k => $v){
            $res[$k] = $data[$v];
        }
        return $res;
    }
    /**
     *  生成发送的数据,可以签名等
     * @param  array   $data       [要发送的数据]
     * @param  bool    $encryption [是否加密]
     * @param  string  $sign       [签名的方法名]
     * @param  string  $ver        [签名的版本]
     * @return array               [description]
     */
    public static function createData(array $data){
        $data['encryption'] = 'off';
        $data['sendTime']   = NOW;
        $data['expiry']     = 0;
        $data['sign'] = 'signPublic';
        //生成appkey
        $data['appkey'] = self::{$data['sign']}( $data['sendTime'], $data['ver'], $data['encryption'], $data['module'], $data['controller'], $data['action'] );
        return $data;
    }

    /**
     * 把数据发送给指定url,示例如下
     *  $url = 'http://bigmall.local/api_1.html';
     *  $data = array(
     *       "module"=>"user",
     *       "controller"=>"apiuser",
     *       "action"=>"userInfo",
     *       "textData"=>'{"userId":"1"}'
     *   );
     *  $sendObj = new ApiBase();
     *  $data = $sendObj->createData( $data, true );
     *  $res = $sendObj->sendData($url,$data );
     *
     * @param  array    $data       [要发送的数据,数组,键值对]
     * @return bool     $res        [返回发送状态]
     */
    public static function sendData(string $url, $data){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_COOKIESESSION, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_TIMEOUT, 600);
        //curl_setopt($ch, CURLOPT_USERAGENT, _USERAGENT_);
        //curl_setopt($ch, CURLOPT_REFERER,_REFERER_);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);
        return json_decode( $output, true );
    }
    /*单个发送的*/
    public static function sendDataOne(string $url, $data){
        $res = self :: sendData($url, $data);
        return $res['res'][0];
    }
    /*多个发送的*/
    public static function sendDataLots(string $url, $data){
        $res = self :: sendData($url, $data);
        if($res['failure'] > 0){
            $res['msg'] = $res['res'][$res['failureIndex']]['msg'];
            $res['code'] = $res['res'][$res['failureIndex']]['code'];
        }else{
            $res['msg'] = '操作成功';
            $res['code'] = SUCCESS_CODE;
        }
        $newRes = [];
        foreach ($res['res'] as $k => $v) {
            $newRes[$k] = $v['res'];
        }
        $res['res'] = $newRes;
        return $res;
    }

    // 获取一个token
    public static function getToken(){
        $proxyQueueUrl = config('proxy_queue_url');
        $data['query'] = json_encode([
                [
                    'path' => 'serveruser/auth/Token/create',
                    'param' =>
                    [
                        'create_ip' => request()->ip(),
                        'create_app_key' => self::getAppKey(),
                        'other_info' => '{}'
                    ]
                ]
            ]);
        $data['success'] = '1';

        return self::sendData($proxyQueueUrl, $data);
    }
    // 获取一个app_key app_key以后可以从redis中读取
    public static function getAppKey() {
        return config('yayi_admin_app_key');
    }
}